﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Mainform.cs" company="company">
//   None
// </copyright>
// <summary>
//   Main application window
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Eeeek
{
    using System;
    using System.ComponentModel;
    using System.Drawing;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Threading;
    using System.Windows.Forms;
    using Eeeek.ExtensionMethods;
    using Eeeek.Properties;
    using Eeeek.TriggeredActions;

    using Microsoft.WindowsAPICodePack.Taskbar;
    using StatePersistence.Implementation;
    using StatePersistence.Interface;

    /// <summary>
    /// Main application window
    /// </summary>
    public partial class MainForm : Form
    {
        #region Fields

        /// <summary>
        /// The form's title bar text when in Batch Mode.
        /// </summary>
        private const string BatchModeTitleText = @"Eeeek! Mouse Automation  (Batch Mode: Ctrl + P to pause)";

        /// <summary>
        /// The form's title bar text when in Trigger Mode.
        /// </summary>
        private const string TriggerModeTitleText = @"Eeeek! Mouse Automation  (Trigger Mode)";

        /// <summary>
        /// The txt to display when summing total iteration time
        /// </summary>
        private const string LoopTimeLabelText = @"Total Loop Time";

        /// <summary>
        /// the text to display the time for all iterations.
        /// </summary>
        private const string TotalTimeLabelText = @"Total Time";

        /// <summary>
        /// The name of the X column
        /// </summary>
        private const string LocationXColumnName = @"X";

        /// <summary>
        /// the name of the Y column
        /// </summary>
        private const string LocationYColumnName = @"Y";

        /// <summary>
        /// The name/title of the delay grid column
        /// </summary>
        private const string DelayColumnName = @"Delay after (ms)";

        /// <summary>
        /// The text to use on the menu item to clear old sessions
        /// </summary>
        private const string ClearOldSessionsMenuText = @"Clear Saved Sessions";

        /// <summary>
        /// The text to use on the menu item to save the current session
        /// </summary>
        private const string SaveSessionMenuText = @"Save Session";

        /// <summary>
        /// The message box text to show for not implemented features
        /// </summary>
        private const string NotImplementedMessage = "LOADING PLEASE WAIT...\r\nOK, don't. I haven't done it yet";

        /// <summary>
        /// A reference to the settings manager
        /// </summary>
        private readonly ISettingsManager<BatchJobData> persistenceManager = new SettingsManager<BatchJobData>(Assembly.GetExecutingAssembly().ManifestModule.Name);

        /// <summary>
        /// A reference to the windowsTaskbar manager (Win7 only)
        /// </summary>
        private readonly TaskbarManager windowsTaskbar = TaskbarManager.Instance;

        /// <summary>
        /// The configuration file settings
        /// </summary>
        private readonly ConfigurationFileSettings configurationFileSettings = new ConfigurationFileSettings(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);

        /// <summary>
        /// The current batch of click operations
        /// </summary>
        private BatchJobData batch;

        /// <summary>
        /// The binding source for the grid
        /// </summary>
        private BindingList<MainFormDataGridRow> bindingSource = new BindingList<MainFormDataGridRow>();

        /// <summary>
        /// A reference to the HotKeyManager
        /// </summary>
        private HotKeyManager hotKeyManager;

        /// <summary>
        /// A counter for the number of loops
        /// </summary>
        private int iterations;

        /// <summary>
        /// A reference to the text used as a trigger for the defined set of clicks.
        /// </summary>
        private string currentTriggerText;

        /// <summary>
        /// A reference to the log file.
        /// </summary>
        private FileInfo logFile;

        /// <summary>
        /// A reference to the log file watcher
        /// </summary>
        private LogFileWatcher watcher;

        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="MainForm"/> class.
        /// </summary>
        public MainForm()
        {
            this.InitializeComponent();
            this.InitializeGridView();
            this.InitializeHotKey();
            this.InitializeFormValues();
            this.PopulateRestoreMenu();
            this.LoadSettings();
        }

        /// <summary>
        /// Gets or sets the current batch data.
        /// </summary>
        /// <value>
        /// The current batch data.
        /// </value>
        private BatchJobData CurrentBatchData
        {
            get
            {
                // ReSharper disable ConvertIfStatementToNullCoalescingExpression
                if (this.batch == null)
                // ReSharper restore ConvertIfStatementToNullCoalescingExpression
                {
                    this.iterations = 0;
                    this.batch = new BatchJobData
                    {
                        Number = this.persistenceManager.MaxSettingsNumber + 1,
                        TimeStamp = DateTime.Now,
                        IterationDelayInSeconds = (int)this.nudRepeatDelay.Value,
                        TotalIterations = (int)this.nudTotalLoops.Value,
                        Clicks = this.bindingSource.ToList(),
                        Name = DateTime.Now.ToString("yyyy-MM-ddTHHmmss")
                    };
                }

                return this.batch;
            }

            set
            {
                this.batch = value;
            }
        }

        /// <summary>
        /// Gets the iteration delay in milliseconds.
        /// </summary>
        private int IterationDelayInMilliseconds
        {
            get
            {
                if (this.CurrentBatchData.IterationDelayInSeconds == 0)
                {
                    return 10;
                }

                return this.CurrentBatchData.IterationDelayInSeconds * 1000;
            }
        }

        #region HotKey hooking

        /// <summary>
        /// Sets up hot key
        /// </summary>
        /// <param name="message">The Windows <see cref="T:System.Windows.Forms.Message"/> to process.</param>
        protected override void WndProc(ref Message message)
        {
            this.SetHotKeyHook(ref message);
        }

        /// <summary>
        /// Initializes the hot key.
        /// </summary>
        private void InitializeHotKey()
        {
            this.hotKeyManager = new HotKeyManager();
            this.hotKeyManager.RegisterGlobalHotKey((int)Keys.P, HotKeyManager.MOD_CONTROL, this.Handle);
        }

        /// <summary>
        /// Sets the hot key hook.
        /// </summary>
        /// <param name="message">The message.</param>
        private void SetHotKeyHook(ref Message message)
        {
            switch (message.Msg)
            {
                case HotKeyManager.WM_HOTKEY:
                    {
                        if ((short)message.WParam == this.hotKeyManager.HotkeyID)
                        {
                            this.EndWorker();
                            this.btnStart.Text = Resources.MainForm_SetHotKeyHook_Resume;
                            this.MoveToForeground();
                        }

                        break;
                    }

                default:
                    {
                        base.WndProc(ref message);
                        break;
                    }
            }
        }

        #endregion

        /// <summary>
        /// Loads the settings.
        /// </summary>
        private void LoadSettings()
        {
            this.logFile = this.configurationFileSettings.LogFile;

            if (this.logFile == null)
            {
                this.stopWhenTrivialToolStripMenuItem.Enabled = false;
                this.stopOnMissingComponentsToolStripMenuItem.Enabled = false;
            }
        }
        
        #region Background worker

        /// <summary>
        /// Stops this instance.
        /// </summary>
        private void Stop()
        {
            this.EndWorker();
            this.MoveToForeground();
        }

        /// <summary>
        /// Ends the worker.
        /// </summary>
        private void EndWorker()
        {
            this.windowsTaskbar.SetOverlayIcon(null, null);

            if (this.worker != null && this.worker.IsBusy)
            {
                this.worker.CancelAsync();
            }

            if (this.worker != null)
            {
                this.worker.Dispose();
            }
        }

        /// <summary>
        /// Handles the worker's Do Work event
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.ComponentModel.DoWorkEventArgs"/> instance containing the event data.</param>
        private void WorkerDoWork(object sender, DoWorkEventArgs e)
        {
            this.StartMacro();
        }

        /// <summary>
        /// Handles the run worker completed event.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.ComponentModel.RunWorkerCompletedEventArgs"/> instance containing the event data.</param>
        private void WorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.EndWorker();
        }

        #endregion

        /// <summary>
        /// Clears the taskbar progress.
        /// </summary>
        private void ClearTaskbarProgress()
        {
            this.windowsTaskbar.SetOverlayIcon(null, null);
            this.windowsTaskbar.SetProgressState(TaskbarProgressBarState.NoProgress);
        }

        /// <summary>
        /// Initializes the task bar reporting.
        /// </summary>
        private void InititalizeTaskBarReporting()
        {
            if (TaskbarManager.IsPlatformSupported && this.nudTotalLoops.Value > 0)
            {
                this.windowsTaskbar.ApplicationId = Assembly.GetExecutingAssembly().FullName;
                this.worker.ProgressChanged += (sender, e) => this.ReportProgress(e);
                this.windowsTaskbar.SetProgressState(TaskbarProgressBarState.Normal);
            }

            Stream iconStream = this.GetType().Assembly.GetManifestResourceStream("Eeeek.Resources.playing.ico");
            if (iconStream != null)
            {
                this.windowsTaskbar.SetOverlayIcon(new Icon(iconStream), Resources.MainForm_ButtonStartClick_Running___);
            }
        }

        /// <summary>
        /// Reports the progress.
        /// </summary>
        /// <param name="eventArgs">The <see cref="System.ComponentModel.ProgressChangedEventArgs"/> instance containing the event data.</param>
        private void ReportProgress(ProgressChangedEventArgs eventArgs)
        {
            this.windowsTaskbar.SetProgressValue(eventArgs.ProgressPercentage, 100);
        }

        /// <summary>
        /// Populates the restore menu.
        /// </summary>
        private void PopulateRestoreMenu()
        {
            this.RestoreSessionToolStripMenuItem.DropDownItems.Clear();

            var saveSubItem = new ToolStripMenuItem(SaveSessionMenuText);
            saveSubItem.Click += (sender, e) => this.SaveSession();
            this.RestoreSessionToolStripMenuItem.DropDownItems.Add(saveSubItem);

            var allPreviousSessions = this.persistenceManager.ListStoredSettings();
            var sortedPreviousSessions = allPreviousSessions.OrderByDescending(s => s.Number);
            var recentSessions = sortedPreviousSessions.Take(this.configurationFileSettings.NumberOfPreviousSessionsInTheMenu).ToArray();

            foreach (var batchJobData in recentSessions)
            {
                var subItem = new ToolStripMenuItem(batchJobData.TimeStamp.ToLocalTime().ToString(CultureInfo.CurrentUICulture)) { Tag = batchJobData };
                subItem.Click += (sender, e) =>
                {
                    var menuItem = sender as ToolStripMenuItem;
                    if (menuItem != null)
                    {
                        var session = menuItem.Tag as BatchJobData;
                        if (session != null)
                        {
                            this.LoadSavedSession(session);
                        }
                    }
                };

                this.RestoreSessionToolStripMenuItem.DropDownItems.Add(subItem);
            }

            var clearSubItem = new ToolStripMenuItem(ClearOldSessionsMenuText);
            clearSubItem.Click += (sender, e) => this.ClearSavedSessions();
            clearSubItem.Enabled = recentSessions.Any();
            this.RestoreSessionToolStripMenuItem.DropDownItems.Add(clearSubItem);
        }

        /// <summary>
        /// Saves the session.
        /// </summary>
        private void SaveSession()
        {
            using (var sessionForm = new SessionForm())
            {
                if (sessionForm.ShowDialog() == DialogResult.OK)
                {
                    string result = sessionForm.SessionName;
                    if (!string.IsNullOrWhiteSpace(result))
                    {
                        this.CurrentBatchData.Name = sessionForm.SessionName;
                        this.persistenceManager.Store(this.CurrentBatchData);
                    }
                }
            }
        }

        /// <summary>
        /// Clears the saved sessions.
        /// </summary>
        private void ClearSavedSessions()
        {
            this.persistenceManager.ClearStoredSettings();
            this.PopulateRestoreMenu();
        }

        /// <summary>
        /// Loads the saved session.
        /// </summary>
        /// <param name="session">The session.</param>
        private void LoadSavedSession(BatchJobData session)
        {
            this.batch = null;
            this.bindingSource = session.ClicksAsBindingList;
            this.nudRepeatDelay.Value = session.IterationDelayInSeconds;
            this.nudTotalLoops.Value = session.TotalIterations;
            this.ActionsGridView.DataSource = this.bindingSource;
        }

        /// <summary>
        /// Initializes the form values.
        /// </summary>
        private void InitializeFormValues()
        {
            this.nudRepeatDelay.Value = this.configurationFileSettings.DefaultDelayBetweenIterationsInSeconds;
            this.nudTotalLoops.Value = this.configurationFileSettings.DefaultNumberOfRepetitions;

            this.batchToolStripMenuItem.Checked = true;
        }

        /// <summary>
        /// Initialize the grid view.
        /// </summary>
        private void InitializeGridView()
        {
            this.bindingSource.Clear();
            this.ActionsGridView.Columns.Clear();
            this.ActionsGridView.AutoGenerateColumns = false;
            DataGridViewColumn horizontalPointColumn = new DataGridViewTextBoxColumn { DataPropertyName = "LocationX", Name = LocationXColumnName, ReadOnly = true, ValueType = typeof(string) };
            this.ActionsGridView.Columns.Add(horizontalPointColumn);
            DataGridViewColumn verticalPointColumn = new DataGridViewTextBoxColumn { DataPropertyName = "LocationY", Name = LocationYColumnName, ReadOnly = true, ValueType = typeof(string) };
            this.ActionsGridView.Columns.Add(verticalPointColumn);
            var delayColumn = new DataGridViewTextBoxColumn { DataPropertyName = "DelayInMilliseconds", Name = DelayColumnName, ReadOnly = false, ValueType = typeof(int) };
            this.ActionsGridView.Columns.Add(delayColumn);
            this.ActionsGridView.DataSource = this.bindingSource;
        }

        /// <summary>
        /// Attempts to restore the target window and and move it to the foreground.
        /// </summary>
        private void AttemptToMoveTargetWindowToForeground()
        {
            int pointer = NativeMethods.FindWindow(null, this.configurationFileSettings.TargetWindowTitle);
            if (pointer > 0)
            {
                NativeMethods.ShowWindow((IntPtr)pointer, NativeMethods.SW_RESTORE);
                NativeMethods.SetForegroundWindow(pointer);
            }
        }

        /// <summary>
        /// Starts the macro.
        /// </summary>
        private void StartMacro()
        {
            // First iteration seems to fail quite often, use a short delay for the target window to become ready
            Thread.Sleep(this.configurationFileSettings.PrebatchStartDelayInSeconds * 1000);
            this.StartProcessingBatch();
            this.MoveToForeground();
        }

        /// <summary>
        /// Displays the total iteration time.
        /// </summary>
        private void DisplayJobTimes()
        {
            double totalTimeMilliseconds = 0;
            totalTimeMilliseconds += Convert.ToInt32(this.nudRepeatDelay.Value) * 1000;
            totalTimeMilliseconds += this.bindingSource.Sum(mainFormDataGridRow => mainFormDataGridRow.DelayInMilliseconds);
            TimeSpan loopTimeSpan = TimeSpan.FromMilliseconds(totalTimeMilliseconds);
            this.lblTotalLoopTime.Text = string.Format("{0} {1}", LoopTimeLabelText, loopTimeSpan.ToFormattedString());
            totalTimeMilliseconds = totalTimeMilliseconds * (double)this.nudTotalLoops.Value;
            TimeSpan totalTimeSpan = TimeSpan.FromMilliseconds(totalTimeMilliseconds);
            this.lblTotalTime.Text = string.Format("{0} {1}", TotalTimeLabelText, totalTimeSpan.ToFormattedString());
        }

        /// <summary>
        /// Verifies the readiness.
        /// </summary>
        private void VerifyReadiness()
        {
            var ready = this.bindingSource.Any();
            this.btnStart.Enabled = ready;
            this.stopWhenTrivialToolStripMenuItem.Enabled = ready;
            this.stopOnMissingComponentsToolStripMenuItem.Enabled = ready;
        }

        /// <summary>
        /// Handles the main form closing.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.FormClosingEventArgs"/> instance containing the event data.</param>
        private void MainFormFormClosing(object sender, FormClosingEventArgs e)
        {
            // Save form state
            if (this.bindingSource.Count > 0)
            {
                this.SaveFormState();
            }

            // Unhook and tidy up
            this.hotKeyManager.UnregisterGlobalHotKey();
            this.EndWorker();

            if (this.watcher != null)
            {
                this.watcher.Dispose();
            }
        }

        /// <summary>
        /// Saves the state of the form.
        /// </summary>
        private void SaveFormState()
        {
            this.persistenceManager.Store(this.CurrentBatchData);
        }

        /// <summary>
        /// Refreshes the form.
        /// </summary>
        private void RefreshForm()
        {
            this.CurrentBatchData = null;
            this.DisplayJobTimes();
            if (this.Visible)
            {
                this.ClearTaskbarProgress();
            }

            this.VerifyReadiness();
        }

        /// <summary>
        /// Starts the processing batch.
        /// </summary>
        private void StartProcessingBatch()
        {
            while (this.CurrentBatchData.TotalIterations == 0 || this.iterations <= this.CurrentBatchData.TotalIterations - 1)
            {
                if (!this.worker.CancellationPending)
                {
                    foreach (var click in this.CurrentBatchData.Clicks)
                    {
                        if (click != null)
                        {
                            if (this.worker.CancellationPending)
                            {
                                break;
                            }

                            IMouseController controller = new MouseController(this.configurationFileSettings.MouseButtonUpDownDelayInMilliseconds);
                            controller.SendMouseClickToPoint(click.Action, new Point { X = click.LocationX, Y = click.LocationY });
                            Thread.Sleep(click.DelayInMilliseconds);
                        }
                    }
                }
                else
                {
                    break;
                }

                this.iterations++;

                if (this.worker.WorkerReportsProgress && this.CurrentBatchData.TotalIterations > 0)
                {
                    decimal progress = decimal.Divide(this.iterations, this.CurrentBatchData.TotalIterations);
                    int progressPercent = Convert.ToInt32(progress * 100);
                    this.worker.ReportProgress(progressPercent);
                }

                Thread.Sleep(this.IterationDelayInMilliseconds);
            }

            this.Stop();
            this.iterations = 0;

            if (btnStart.InvokeRequired)
            {
                this.btnStart.Invoke(new MethodInvoker(() => this.btnStart.Text = Resources.MainForm_StartProcessingBatch_Start));
            }
            else
            {
                this.btnStart.Text = Resources.MainForm_StartProcessingBatch_Start;
            }
        }

        /// <summary>
        /// Moves to foreground.
        /// </summary>
        private void MoveToForeground()
        {
            this.Invoke(new MethodInvoker(() => NativeMethods.ShowWindow(this.Handle, NativeMethods.SW_RESTORE)));
            this.Invoke(new MethodInvoker(() => NativeMethods.SetForegroundWindow((int)this.Handle)));
        }

        #region Settings

        /// <summary>
        /// Shows the settings dialog.
        /// </summary>
        /// <returns>[True] if DialogResult.OK</returns>
        private bool ShowSettingsDialog()
        {
            using (var dialog = new SettingsForm())
            {
                if (dialog.ShowDialog() == DialogResult.OK)
                {
                    this.configurationFileSettings.DefaultDelayBetweenClicksInMilliseconds = dialog.DefaultDelayBetweenClicksInMilliseconds;
                    this.configurationFileSettings.DefaultDelayBetweenIterationsInSeconds = dialog.DefaultDelayBetweenIterations;
                    this.configurationFileSettings.DefaultNumberOfRepetitions = dialog.DefaultNumberOfRepetitions;
                    this.configurationFileSettings.TargetWindowTitle = dialog.TargetApplicationToUse;
                    this.configurationFileSettings.NumberOfPreviousSessionsInTheMenu = dialog.SizeOfSessionMenu;
                    this.configurationFileSettings.MouseButtonUpDownDelayInMilliseconds = dialog.ClickDownUpDelay;
                    this.configurationFileSettings.PrebatchStartDelayInSeconds = dialog.BatchStartDelayInSeconds;
                    this.configurationFileSettings.LogFile = dialog.LogFile;
                    return true;
                }

                return false;
            }
        }

        #endregion

        #region Triggers

        /// <summary>
        /// Unchecks the trigger menu sub-items.
        /// </summary>
        private void UncheckModeMenuItems()
        {
            foreach (ToolStripMenuItem menuItem in modeToolStripMenuItem.DropDownItems)
            {
                menuItem.Checked = false;
            }
        }

        /// <summary>
        /// Setups the log file watcher.
        /// </summary>
        private void SetupLogFileWatcher()
        {
            if (this.logFile == null)
            {
                return;
            }

            this.watcher = new LogFileWatcher
                {
                    Path = this.logFile.DirectoryName,
                    Filter = this.logFile.Name,
                    NotifyFilter = NotifyFilters.LastWrite,
                    IncludeSubdirectories = false,
                    EnableRaisingEvents = true,
                    Trigger = this.currentTriggerText
                };

            this.watcher.Start(this.logFile);
            this.watcher.Changed += this.OnLogFileChanged;
        }

        /// <summary>
        /// Called when [log file changed].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.IO.FileSystemEventArgs"/> instance containing the event data.</param>
        private void OnLogFileChanged(object sender, FileSystemEventArgs e)
        {
            try
            {
                this.watcher.EnableRaisingEvents = false;
                if (this.watcher.FileChangeIncludesTrigger(e))
                {
                    if (this.worker.IsBusy)
                    {
                        this.PlayAlertSound();
                        this.Stop();
                    }
                    else
                    {
                        this.StartProcessingBatch();
                    }
                }
            }
            finally
            {
                this.watcher.EnableRaisingEvents = true;
            }
        }

        /// <summary>
        /// Plays the alert sound.
        /// </summary>
        private void PlayAlertSound()
        {
            throw new NotImplementedException();
        }

        #endregion

        #region Interface event handlers

        /// <summary>
        /// Handles the Click event of the EditSettingsToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void EditSettingsToolStripMenuItemClick(object sender, EventArgs e)
        {
            this.ShowSettingsDialog();
        }

        /// <summary>
        /// Handles the close button click event
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void ButtonCloseClick(object sender, EventArgs e)
        {
            this.EndWorker();
            this.Close();
        }

        /// <summary>
        /// Handles the mouse moving over the transparent panel
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.MouseEventArgs"/> instance containing the event data.</param>
        private void TransparentPanelMouseMove(object sender, MouseEventArgs e)
        {
            int topOfPanel = this.TransparentPanel.Location.Y;
            int leftOfPanel = this.TransparentPanel.Location.X;
            int topOfForm = this.Location.Y;
            int leftOfForm = this.Location.X;

            this.ToolStripCursorLocation.Text = string.Format("X:{0}, Y:{1}", leftOfPanel + leftOfForm + e.X, topOfPanel + topOfForm + e.Y);
        }

        /// <summary>
        /// Handles a mouse click in the transparent panel
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.MouseEventArgs"/> instance containing the event data.</param>
        private void TransparentPanelMouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                this.bindingSource.Add(new MainFormDataGridRow(MouseAction.LeftClick, Cursor.Position, this.configurationFileSettings.DefaultDelayBetweenClicksInMilliseconds));
            }
        }

        /// <summary>
        /// Handles the grid view rows added.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewRowsAddedEventArgs"/> instance containing the event data.</param>
        private void ActionsGridViewRowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
        {
            this.RefreshForm();
        }

        /// <summary>
        /// Handles the ValueChanged event of the RepeatDelay control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void NudRepeatDelayValueChanged(object sender, EventArgs e)
        {
            this.RefreshForm();
        }

        /// <summary>
        /// Handles the grid view rows removed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewRowsRemovedEventArgs"/> instance containing the event data.</param>
        private void ActionsGridViewRowsRemoved(object sender, DataGridViewRowsRemovedEventArgs e)
        {
            this.RefreshForm();
        }

        /// <summary>
        /// Handles the grid view cell value changed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewCellEventArgs"/> instance containing the event data.</param>
        private void ActionsGridViewCellValueChanged(object sender, DataGridViewCellEventArgs e)
        {
            this.RefreshForm();
        }

        /// <summary>
        /// Handles the ValueChanged event of the totalIterations control.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void NudTotalLoopsValueChanged(object sender, EventArgs e)
        {
            this.RefreshForm();
        }

        /// <summary>
        /// Handles the Click event of the aboutToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void AboutToolStripMenuItemClick(object sender, EventArgs e)
        {
            MessageBox.Show(NotImplementedMessage);
        }

        /// <summary>
        /// Shows the help tool strip menu item click.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void ShowHelpToolStripMenuItemClick(object sender, EventArgs e)
        {
            MessageBox.Show(NotImplementedMessage);
        }

        /// <summary>
        /// Handles the click event of the clear button
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void BtnClearClick(object sender, EventArgs e)
        {
            this.CurrentBatchData = null;
            this.InitializeGridView();
            this.InitializeFormValues();
            this.InititalizeTaskBarReporting();
        }

        /// <summary>
        /// Handles the Click event of the triggerToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void TriggerToolStripMenuItemClick(object sender, EventArgs e)
        {
            this.UncheckModeMenuItems();
            this.triggerToolStripMenuItem.Checked = true;
            this.Text = TriggerModeTitleText;

            this.nudRepeatDelay.Value = 0;
            this.nudRepeatDelay.Enabled = false;

            this.nudTotalLoops.Value = 1;
            this.nudTotalLoops.Enabled = false;
        }

        /// <summary>
        /// Handles the Click event of the batchToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void BatchToolStripMenuItemClick(object sender, EventArgs e)
        {
            this.UncheckModeMenuItems();
            this.batchToolStripMenuItem.Checked = true;
            this.Text = BatchModeTitleText;

            this.nudRepeatDelay.Value = this.configurationFileSettings.DefaultDelayBetweenIterationsInSeconds;
            this.nudRepeatDelay.Enabled = true;

            this.nudTotalLoops.Value = this.configurationFileSettings.DefaultNumberOfRepetitions;
            this.nudTotalLoops.Enabled = true;
        }

        #endregion

        /// <summary>
        /// Handles the stop when trivial menu item's click event
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void StopWhenTrivialToolStripMenuItemClick(object sender, EventArgs e)
        {
            if (!this.VerifyLogFile())
            {
                return;
            }

            this.currentTriggerText = "You can no longer advance your skills";
            this.SetupLogFileWatcher();
            this.Start();
        }

        /// <summary>
        /// Handles the Click event of the stopOnMissingComponentsToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void StopOnMissingComponentsToolStripMenuItemClick(object sender, EventArgs e)
        {
            if (!this.VerifyLogFile())
            {
                return;
            }

            this.currentTriggerText = "You do not have everything";
            this.SetupLogFileWatcher();
            this.Start();
        }

        /// <summary>
        /// Verifies the log file.
        /// </summary>
        /// <returns>Boolean [True] if OK</returns>
        private bool VerifyLogFile()
        {
            if (this.logFile == null)
            {
                var result = this.ShowSettingsDialog();
                this.LoadSettings();
                return result;
            }

            return true;
        }

        /// <summary>
        /// Handles the ButtonClick event of the start button control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void ButtonStartButtonClick(object sender, EventArgs e)
        {
            this.Start();
            this.worker.ReportProgress(0);
        }

        /// <summary>
        /// Starts this instance.
        /// </summary>
        private void Start()
        {
            this.AttemptToMoveTargetWindowToForeground();
            this.WindowState = FormWindowState.Minimized;

            this.InititalizeTaskBarReporting();
            this.AttemptToMoveTargetWindowToForeground();
            this.worker.WorkerReportsProgress = this.CurrentBatchData.TotalIterations > 0;

            this.worker.RunWorkerAsync();
        }
    }
}
